/**
 * 
 */
package org.stars.daostars.starterkit;

import java.io.ByteArrayInputStream;
import java.sql.Blob;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;

import org.apache.commons.dbutils.DbUtils;
import org.apache.commons.logging.Log;

import org.stars.daostars.DaoBase;
import org.stars.daostars.starterkit.EntityBeanBase;
import org.stars.util.StringUtility;


/**
 * @author Francesco Benincasa (908099)
 * @param <E> 
 *
 */ 
public class DaoGeneric<E extends EntityBeanBase> extends DaoBase<E> {		

	public DaoGeneric(Class<E> eType) {
		super(eType);		
	}
	
	public DaoGeneric(Class<E> eType, String connectionName) {
		super(eType, connectionName);		
	}
	
	public static final String UPDATE_BLOB = "UPDATE_BLOB";

	public static final String SELECT_BLOB = "SELECT_BLOB";

	/**
	 * serial id
	 */
	private static final long serialVersionUID = -9157231179433889975L;

    /**
     * Effettua la cancellazione di un elemento.
     * 
     * Richiama le seguenti query:
     * <ul>
     * 	<li>SQL_DELETE: cancella l'elemento</li>
     * </ul>
     * 
     * @param entity
     * 			eb da cancellare
     * @return
     * 			<code>true</code> se l'operazione va bene, <code>false</code> in caso
     * 			di errore
     * @throws Exception
     * 			eccezione legati all'esecuzione del codice sql
     */
    public int elimina(E entity) throws Exception  {
    	return execute("SQL_DELETE",null,entity, null);
    }	
	
    
	/**
	 * Recupera il sequence attuale.
	 * 
	 * @return sequence attuale
	 * @throws Exception
	 *             in caso di errore
	 */
	protected void getSequence(E[] dto) throws Exception {				
		Connection conn = null;
		PreparedStatement st = null;
		ResultSet rs = null;
		long sq = -1;
			try {
				conn = getConnection();
				st = conn.prepareStatement(getQuerySQL(SQL_UNIQUE_ID));

				for (E item : dto) {
					rs = st.executeQuery();
					if (rs.next()) {
						sq = rs.getInt("name");
					}

					item.setId(sq);

					if (rs != null) {
						rs.close();
					}
				}

			} catch (SQLException sqle) {
				Log log=getLog();
				log.fatal(sqle.getMessage());

			} finally {
				if (rs != null) {
					rs.close();
				}
				if (st != null) {
					st.close();
				}
				if (conn != null) {
					conn.close();
				}
			}
	}
    
    /**
     * Effettua l'inserimento di un elemento.
     * 
     * Richiama le seguenti query:
     * <ul>
     * 	<li>SQL_UNIQUE_ID: carica l'id dell'elemento</li>
     * 	<li>SQL_INSERT: inserisce l'elemento valorizzando la propriet� <code>id</code> con
     * 		il risultato della query <code>SQL_UNIQUE_ID</code>.</li>
     * </ul>
     * 
     * @param entity
     * 			eb da inserire
     * @return
     * 			<code>true</code> se l'operazione va bene, <code>false</code> in caso
     * 			di errore
     * @throws Exception
     * 			eccezione legati all'esecuzione del codice sql
     */
    public boolean crea(E entity) throws Exception {
    	boolean bRet;
    	boolean bUsatoNuovoId=false;
    	Long	nOldId=null;
    	
    	try {
    		int ret=read(SQL_UNIQUE_ID,null,entity, null,null);
    		
    		if (ret==1) {	
    			bUsatoNuovoId=true;
    			nOldId=entity.getId();
    			entity.setId(((EntityBeanBase) dettaglioBean).getId());
    		}
    		bRet= execute(SQL_INSERT,null,  entity, null) > 0;
    		
    	} catch(Exception e) {
    		printDebugSQL("DaoGeneric Errore: "+e.toString());
    		if (bUsatoNuovoId) {
    			entity.setId(nOldId);
    		}
    		throw(e);
    	}    	
    	
    	return bRet;
    }
    
	/**
	 * Crea mediante l'esecuzioni di query batch nuovi elementi
	 * 
	 * @param dto
	 * @return
	 * @throws Exception
	 */
	public boolean creaBatch(E[] dto) throws Exception {

		getSequence(dto);
		int ret = executeBatch(SQL_INSERT, dto);

		return ret == 1;
	}
	
		
	//TODO: valido
	public int esegui(String sSqlName, E entity) throws Exception
	{ 
        return execute(sSqlName,null, entity, null);
	}
	
	
	public static final String SQL_UNIQUE_ID	= "SQL_UNIQUE_ID";
	public static final String SQL_UPDATE = "SQL_UPDATE";	
	public static final String SQL_SELECT = "SQL_SELECT";
	public static final String SQL_INSERT = "SQL_INSERT";
	public static final String SQL_DELETE = "SQL_DELETE";
	          
    //TODO: valido
    public int recupera(String sSqlName, E entity) throws Exception {
    	return read(sSqlName,null,entity, null,null);
    }
    
    //TODO: valido
	public int recupera(String sSqlName, E eb, Class<?> resultType) throws Exception
	{	
		return read(sSqlName,null,eb, null,resultType);
	}	
	
    //TODO: valido
	public int recupera(String sSqlName, E eb, Object[] parms,Class<?> resultType) throws Exception
	{	
		return read(sSqlName,null,eb, parms,resultType);
	}

    //TODO: valido
	public int recupera(String sSqlName,String moreSql, E eb,Object[] parms, Class<?> resultType) throws Exception
	{	
		return read(sSqlName,moreSql,eb, null,resultType);
	}		
	       
    //TODO: valido
    public int recupera(String sSqlName, String moreSql, E entity, Object[] parms) throws Exception {
    	return read(sSqlName,moreSql,entity, parms,null);
    }
    
	/**
	 * Ritorna un byte array rappresentante il campo Blob ottenuto eseguendo la
	 * query con chiave: SELECT_BLOB la quale deve avere un unico parametro di
	 * query (id) e ritornare come unico campo il Blob stesso
	 * 
	 * @param id
	 *            chiave di query
	 * @return byte array rappresentante il blob
	 * @throws Exception
	 *             di tipo SQL
	 */
	public byte[] recuperaBlob(Long id) throws Exception {		
		byte[] blob = null;
		if (id != null && id != EntityBeanBase.DEFAULT_ID) {
			Connection conn = null;
			PreparedStatement ps = null;
			ResultSet rs = null;
			try {
				conn = getConnection();
				ps = conn.prepareStatement(getQuerySQL(SELECT_BLOB));

				ps.setObject(1, id);
				rs = ps.executeQuery();

				if (rs.next() && rs.getMetaData().getColumnCount() > 0 && rs.getMetaData().getColumnType(1) == Types.BLOB) {
					Blob tmp = rs.getBlob(1);
					if (!rs.wasNull())
						blob = tmp.getBytes(1, (int) tmp.length());
				}
			} catch (SQLException e) {
				Log log = getLog();
				log.fatal(e.getMessage());

				throw (new SQLException(e.getMessage()));
			} finally {
				DbUtils.closeQuietly(conn, ps, rs);
			}
		}

		return blob;
	}
    
	/**
	 * Aggiorna il byte array di dati passato in un campo blob utilizzando la
	 * chiave d query UPDATE_BLOB la quale deve avere come primo parametro il
	 * campo blob da aggiornare e come ultimo la
	 * 
	 * @ulchiave primaria@ul (id) su cui viene fatta la where di update
	 * 
	 * @param id
	 *            chiave primaria
	 * @param blob
	 *            byte array contenente i dati da memorizzare nel blob
	 * @return boolean true = success
	 * @throws Exception
	 *             di tipo SQL
	 */
	public boolean memorizzaBlob(Long id, byte[] blob) throws Exception {		
		boolean ok = false;
		if (blob != null && id != null && id != EntityBeanBase.DEFAULT_ID) {
			Connection conn = null;
			PreparedStatement ps = null;
			try {
				conn = getConnection();
				String sSql = getQuerySQL(UPDATE_BLOB);

				printDebugStack();
				printDebugSQL("Dao: " + daoClassName + " Query: " + StringUtility.nvl(UPDATE_BLOB, "<Unknown>") + " " + " read SQL: " + sSql);

				ps = conn.prepareStatement(sSql);

				int length = blob.length;

				ByteArrayInputStream inputStream = new ByteArrayInputStream(blob);
				ps.setBinaryStream(1, inputStream, length);

				ps.setObject(2, id);

				ok = (ps.executeUpdate() > 0);				
			} catch (SQLException e) {
				Log log = getLog();
				log.fatal(e);

				throw (new SQLException(e.getMessage()));
			} finally {
				DbUtils.closeQuietly(conn, ps, null);
			}

		}

		return ok;
	}
    
    //TODO: valido
	public int read(String sSqlName, E eb, String whereConditions, Object[] parms,Class<?> resultType) throws Exception
	{	
		return read(sSqlName,whereConditions,eb, parms,resultType);
	}  	

}
